﻿using System;
using System.Globalization;
using System.Text;
using System.IO;
using System.Threading;
using System.Web;
using System.Xml;
using System.Xml.XPath;
using System.Text.RegularExpressions;
using System.Net;


namespace ResxLocGenerator
{
    class ResourceLocal
    {
        private static string _fileIn;
        private static string _fileOut;
        private const string ResxExt = ".resx";
        private const string LocalesFile = "Languages.txt";

        //const
        private const string LocaleDefault = "en";

        private static readonly AutoResetEvent TransalteWait = new AutoResetEvent(false);
        private static string _tranlsatedText;

        // Google Translate REST service URL.
        private const string ServiceUrl = "https://www.googleapis.com/language/translate/v2?key={0}&q={1}&source={2}&target={3}";

        // Http request / response manager.
        private static HttpWebRequest Proxy;

        public static void Main(string[] args)
        {
            //parameters
            string localeFrom = null;
            string localeTo = null;

            //Proxy.DownloadStringCompleted += DownloadStringCompleted;

            //Get imput from command line
            if (args.Length != 1)    //Case 1. User didn't provides any files 
            {
                Help();
                return;
            }
            
            _fileIn = args[0];

            if (!File.Exists(_fileIn))
            {
                Console.WriteLine("Error: The provided file is not exist!");
                return;
            }

            if (!_fileIn.Contains(ResxExt))
            {
                Console.WriteLine("Error: The provided file is not a valid resource file!");
                Console.WriteLine("Only files with extention '" + ResxExt + "' are valid.");
                Console.WriteLine("Type ResxLocGenerator /? for more info.");
                return;
            }

            if (args.Length == 1)  //Case 2. Resource file only
            {
                //detect source language from the file name, if possible
                localeFrom = DetectTheSourceLanguage(_fileIn);
            }
            
            SublocalizeFileOrDirectory(_fileIn, localeFrom, localeTo);

            Console.WriteLine("Finished");
            Console.ReadLine();
        }

        private static void Help()
        {
            Console.WriteLine("Please enter the source resource file you would like to translate");
        }


        /// <summary>
        /// This function tries to detect source language from the file name
        /// if language is not porvides, try to get from the file
        /// if no corresponding letters in file name, then use just an English
        /// </summary>
        /// <param name="fileIn"></param>
        /// <returns></returns>
        private static string DetectTheSourceLanguage(string fileIn)
        {
            var fileName = fileIn.Substring(fileIn.LastIndexOf("\\") + 1);  //get the file name

            //remove the tail: .resx 
            var head = fileName.Substring(0, fileName.LastIndexOf("."));

            //check if this file name contains locale
            var localeFrom = head.Contains(".") ? head.Substring(head.LastIndexOf(".") + 1) : LocaleDefault;
            return localeFrom;
        }

        private static void SublocalizeFileOrDirectory(string source, string localeFrom, string localeTo)
        {

            string[] locales = null;

            //Case when user need to translate to a specific locale
            if (localeTo != null)
            {
                locales = new string[1];
                locales[0] = localeTo;
            }
            else //translate to all languages
            {
                //Get languages from the SOAP Web Service
                //Add this functionality later.  If needed  locales = GetListOfLanguagesFromWebService();
                if (locales == null) //Use hardcoded list of existing languages from the provided text file
                {
                    locales = File.ReadAllLines(LocalesFile);
                }
            }

            //Create resource file for each locale
            for (int i = 0; i < locales.Length; i++)
            {
                bool ret = SubLocalizeResourceFile(source, localeFrom, locales[i]);

                if (!ret)
                {
                    Console.WriteLine("Translation for the file {0} failed, please check your source file.", source);
                    return;
                }
            }

            Console.WriteLine("Translation complete!");
        }

        /// <summary>
        /// Overloaded function, case, when the source file is differ than English
        /// </summary>
        /// <param name="fileIn"></param>
        /// <param name="locale"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private static bool SubLocalizeResourceFile(string fileIn, string sourceLocale, string targetLocale)
        {
            bool ret = true;

            //Create a output directory for the ouptut file
            //string path = fileIn.Substring(0, fileIn.LastIndexOf("\\") + 1) + "Localized\\";
            string path = fileIn.Substring(0, fileIn.LastIndexOf("\\") + 1);

            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            //Create an output file, use target locale as part of the file name
            string fileTempName = fileIn.Substring(fileIn.LastIndexOf("\\") + 1);
            string fileName = fileTempName.Substring(0, fileTempName.IndexOf(".") + 1);
            _fileOut = path + fileName + targetLocale + ResxExt;

            //Delete existing copy of file if it is exists
            if (File.Exists(_fileOut))
            {
                //Delete
                File.Delete(_fileOut);
            }

            if (targetLocale == null)
            {
                Console.WriteLine("Please provide valid target locale");
                return ret;
            }

            //Default source locale is english
            if (sourceLocale == null)
            {
                sourceLocale = "en";
            }

            //Open the XML file "resx" file for reading
            var doc = new XmlDocument();
            try
            {
                doc.Load(fileIn);
            }
            catch
            {
                Console.WriteLine("Error: Resource file is invalid. ");
                return false;
            }

            var navigator = doc.CreateNavigator();
            bool abc = navigator.CanEdit;

            Console.WriteLine("Start to process file {0}", fileIn);

            XPathNodeIterator iterator = navigator.Select("descendant::data/value");


            var manager = new XmlNamespaceManager(navigator.NameTable);

            foreach (XPathNavigator nav in navigator.Select("descendant::data/value", manager))
            {
                string val = nav.Value;

                if (string.IsNullOrEmpty(val))
                {
                    //Get the name of the data
                    Console.WriteLine("The value of the data is empty");
                }
                else
                {
                    var strTarget = TranslateSentence(val, sourceLocale, targetLocale);

                    //Validate translated sentence
                    if (strTarget != null)
                    {
                        //check if it is get translated
                        if (val == strTarget)
                        {
                            SourceAndTargetTheSame(targetLocale, val);
                        }

                        //Put back removede an HTML and XML tags
                        if (strTarget.Contains("&lt;"))
                        {
                            strTarget = UnescapeDataString(strTarget);
                        }

                        //Replace source value in the Resx file to translated value
                        nav.SetValue(strTarget);
                    } //end if
                } //end else
            } //end foreach

            File.WriteAllText(_fileOut, navigator.OuterXml, Encoding.Unicode);
            Console.WriteLine(" "); //live a space
            Console.WriteLine("Success translating file{0} to the language {1}", fileIn, targetLocale);
            Console.WriteLine("File {0} get created.", _fileOut);
            Console.WriteLine(" ");
            return ret;
        }

        private static void SourceAndTargetTheSame(string targetLocale, string val)
        {
            Console.WriteLine("Warning: Source {0} and target sentences are the same for language {1}", val, targetLocale);
        }

        private static string TranslateSentence(string text, string localeFrom, string localeTo)
        {
            //set parameters

            if (string.IsNullOrEmpty(localeFrom))
            {
                //use default - en
                localeFrom = LocaleDefault;
            }

            var targetGoogleLanguageCode = GetGoogleLangaugeCode(localeTo);

            var googleTranslateUrl = string.Format(ServiceUrl, GoogleAPICode.GetDevKey(), HttpUtility.UrlEncode(text), localeFrom, targetGoogleLanguageCode);

            _tranlsatedText = string.Empty;
            Proxy = (HttpWebRequest)WebRequest.Create(new Uri(googleTranslateUrl));
            Proxy.BeginGetResponse(ReadCallback, Proxy);

            TransalteWait.WaitOne();

            var translText = HttpUtility.HtmlDecode(_tranlsatedText);
            translText = ParseAndConvertUnicodeCharacter(translText);


            if (text.ToLower() == "en")
                translText = localeTo;

            return translText;

        }

        private static string GetGoogleLangaugeCode(string localeTo)
        {
            var googleLanguageCode = localeTo;

            switch (localeTo.ToLower())
            {
                case "fil-ph":
                    googleLanguageCode = "fil";
                    break;
                default:
                    break;
            }

            return googleLanguageCode;
        }

        private static string ParseAndConvertUnicodeCharacter(string s)
        {
            string res = s;
            MatchCollection reg = Regex.Matches(res, @"\\u([0-9a-fA-F]{4})");
            for (int i = 0; i < reg.Count; i++)
            {
                res = res.Replace(reg[i].Groups[0].Value, "" +
                (char)int.Parse(reg[i].Groups[1].Value, NumberStyles.HexNumber));
            }
            return res;
        }

        private static void ReadCallback(IAsyncResult asynchronousResult)
        {
            var request = (HttpWebRequest)asynchronousResult.AsyncState;
            var response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);

            using (var streamReader = new StreamReader(response.GetResponseStream()))
            {
                var streamText = streamReader.ReadToEnd();
                _tranlsatedText = Regex.Match(HttpUtility.UrlDecode(streamText), "\"translatedText\": \"(.*?)\"").Groups[1].Value;
                TransalteWait.Set();
            }
        }

        /// <summary>
        /// The UnescapeDataString function, unescaped characters, 
        /// that being escaped (the tags "<" and ">" was substituted to the &..;) by the System.Security.SecurityElement.Escape
        /// SOAPsvc failed, if text contains tags "<, >"
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private static string UnescapeDataString(string text)
        {
            while (text.Contains("&lt;"))
            {
                text = text.Replace("&lt;", "<");
                text = text.Replace("&gt;", ">");
            }
            return text;
        }
    }
}